2 use Wikimedia\Services\ServiceContainer
;
5 * @covers Wikimedia\Services\ServiceContainer
7 class ServiceContainerTest
extends PHPUnit\Framework\TestCase
{
9 use MediaWikiCoversValidator
; // TODO this library is supposed to be independent of MediaWiki
10 use PHPUnit4And6Compat
;
12 private function newServiceContainer( $extraArgs = [] ) {
13 return new ServiceContainer( $extraArgs );
16 public function testGetServiceNames() {
17 $services = $this->newServiceContainer();
18 $names = $services->getServiceNames();
20 $this->assertInternalType( 'array', $names );
21 $this->assertEmpty( $names );
23 $name = 'TestService92834576';
24 $services->defineService( $name, function () {
28 $names = $services->getServiceNames();
29 $this->assertContains( $name, $names );
32 public function testHasService() {
33 $services = $this->newServiceContainer();
35 $name = 'TestService92834576';
36 $this->assertFalse( $services->hasService( $name ) );
38 $services->defineService( $name, function () {
42 $this->assertTrue( $services->hasService( $name ) );
45 public function testGetService() {
46 $services = $this->newServiceContainer( [ 'Foo' ] );
48 $theService = new stdClass();
49 $name = 'TestService92834576';
52 $services->defineService(
54 function ( $actualLocator, $extra ) use ( $services, $theService, &$count ) {
56 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
57 PHPUnit_Framework_Assert
::assertSame( $extra, 'Foo' );
62 $this->assertSame( $theService, $services->getService( $name ) );
64 $services->getService( $name );
65 $this->assertSame( 1, $count, 'instantiator should be called exactly once!' );
68 public function testGetService_fail_unknown() {
69 $services = $this->newServiceContainer();
71 $name = 'TestService92834576';
73 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
75 $services->getService( $name );
78 public function testPeekService() {
79 $services = $this->newServiceContainer();
81 $services->defineService(
84 return new stdClass();
88 $services->defineService(
91 return new stdClass();
95 // trigger instantiation of Foo
96 $services->getService( 'Foo' );
98 $this->assertInternalType(
100 $services->peekService( 'Foo' ),
101 'Peek should return the service object if it had been accessed before.'
105 $services->peekService( 'Bar' ),
106 'Peek should return null if the service was never accessed.'
110 public function testPeekService_fail_unknown() {
111 $services = $this->newServiceContainer();
113 $name = 'TestService92834576';
115 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
117 $services->peekService( $name );
120 public function testDefineService() {
121 $services = $this->newServiceContainer();
123 $theService = new stdClass();
124 $name = 'TestService92834576';
126 $services->defineService( $name, function ( $actualLocator ) use ( $services, $theService ) {
127 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
131 $this->assertTrue( $services->hasService( $name ) );
132 $this->assertSame( $theService, $services->getService( $name ) );
135 public function testDefineService_fail_duplicate() {
136 $services = $this->newServiceContainer();
138 $theService = new stdClass();
139 $name = 'TestService92834576';
141 $services->defineService( $name, function () use ( $theService ) {
145 $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException
::class );
147 $services->defineService( $name, function () use ( $theService ) {
152 public function testApplyWiring() {
153 $services = $this->newServiceContainer();
156 'Foo' => function () {
159 'Bar' => function () {
164 $services->applyWiring( $wiring );
166 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
167 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
170 public function testImportWiring() {
171 $services = $this->newServiceContainer();
174 'Foo' => function () {
177 'Bar' => function () {
180 'Car' => function () {
185 $services->applyWiring( $wiring );
187 $services->addServiceManipulator( 'Foo', function ( $service ) {
188 return $service . '+X';
191 $services->addServiceManipulator( 'Car', function ( $service ) {
192 return $service . '+X';
195 $newServices = $this->newServiceContainer();
197 // create a service with manipulator
198 $newServices->defineService( 'Foo', function () {
202 $newServices->addServiceManipulator( 'Foo', function ( $service ) {
203 return $service . '+Y';
206 // create a service before importing, so we can later check that
207 // existing service instances survive importWiring()
208 $newServices->defineService( 'Car', function () {
212 // force instantiation
213 $newServices->getService( 'Car' );
215 // Define another service, so we can later check that extra wiring
217 $newServices->defineService( 'Xar', function () {
221 // import wiring, but skip `Bar`
222 $newServices->importWiring( $services, [ 'Bar' ] );
224 $this->assertNotContains( 'Bar', $newServices->getServiceNames(), 'Skip `Bar` service' );
225 $this->assertSame( 'Foo!+Y+X', $newServices->getService( 'Foo' ) );
227 // import all wiring, but preserve existing service instance
228 $newServices->importWiring( $services );
230 $this->assertContains( 'Bar', $newServices->getServiceNames(), 'Import all services' );
231 $this->assertSame( 'Bar!', $newServices->getService( 'Bar' ) );
232 $this->assertSame( 'Car!', $newServices->getService( 'Car' ), 'Use existing service instance' );
233 $this->assertSame( 'Xar!', $newServices->getService( 'Xar' ), 'Predefined services are kept' );
236 public function testLoadWiringFiles() {
237 $services = $this->newServiceContainer();
240 __DIR__
. '/TestWiring1.php',
241 __DIR__
. '/TestWiring2.php',
244 $services->loadWiringFiles( $wiringFiles );
246 $this->assertSame( 'Foo!', $services->getService( 'Foo' ) );
247 $this->assertSame( 'Bar!', $services->getService( 'Bar' ) );
250 public function testLoadWiringFiles_fail_duplicate() {
251 $services = $this->newServiceContainer();
254 __DIR__
. '/TestWiring1.php',
255 __DIR__
. '/./TestWiring1.php',
258 // loading the same file twice should fail, because
259 $this->setExpectedException( Wikimedia\Services\ServiceAlreadyDefinedException
::class );
261 $services->loadWiringFiles( $wiringFiles );
264 public function testRedefineService() {
265 $services = $this->newServiceContainer( [ 'Foo' ] );
267 $theService1 = new stdClass();
268 $name = 'TestService92834576';
270 $services->defineService( $name, function () {
271 PHPUnit_Framework_Assert
::fail(
272 'The original instantiator function should not get called'
276 // redefine before instantiation
277 $services->redefineService(
279 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
280 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
281 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
286 // force instantiation, check result
287 $this->assertSame( $theService1, $services->getService( $name ) );
290 public function testRedefineService_disabled() {
291 $services = $this->newServiceContainer( [ 'Foo' ] );
293 $theService1 = new stdClass();
294 $name = 'TestService92834576';
296 $services->defineService( $name, function () {
300 // disable the service. we should be able to redefine it anyway.
301 $services->disableService( $name );
303 $services->redefineService( $name, function () use ( $theService1 ) {
307 // force instantiation, check result
308 $this->assertSame( $theService1, $services->getService( $name ) );
311 public function testRedefineService_fail_undefined() {
312 $services = $this->newServiceContainer();
314 $theService = new stdClass();
315 $name = 'TestService92834576';
317 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
319 $services->redefineService( $name, function () use ( $theService ) {
324 public function testRedefineService_fail_in_use() {
325 $services = $this->newServiceContainer( [ 'Foo' ] );
327 $theService = new stdClass();
328 $name = 'TestService92834576';
330 $services->defineService( $name, function () {
334 // create the service, so it can no longer be redefined
335 $services->getService( $name );
337 $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException
::class );
339 $services->redefineService( $name, function () use ( $theService ) {
344 public function testAddServiceManipulator() {
345 $services = $this->newServiceContainer( [ 'Foo' ] );
347 $theService1 = new stdClass();
348 $theService2 = new stdClass();
349 $name = 'TestService92834576';
351 $services->defineService(
353 function ( $actualLocator, $extra ) use ( $services, $theService1 ) {
354 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
355 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
360 $services->addServiceManipulator(
363 $theService, $actualLocator, $extra
365 $services, $theService1, $theService2
367 PHPUnit_Framework_Assert
::assertSame( $theService1, $theService );
368 PHPUnit_Framework_Assert
::assertSame( $services, $actualLocator );
369 PHPUnit_Framework_Assert
::assertSame( 'Foo', $extra );
374 // force instantiation, check result
375 $this->assertSame( $theService2, $services->getService( $name ) );
378 public function testAddServiceManipulator_fail_undefined() {
379 $services = $this->newServiceContainer();
381 $theService = new stdClass();
382 $name = 'TestService92834576';
384 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
386 $services->addServiceManipulator( $name, function () use ( $theService ) {
391 public function testAddServiceManipulator_fail_in_use() {
392 $services = $this->newServiceContainer( [ 'Foo' ] );
394 $theService = new stdClass();
395 $name = 'TestService92834576';
397 $services->defineService( $name, function () use ( $theService ) {
401 // create the service, so it can no longer be redefined
402 $services->getService( $name );
404 $this->setExpectedException( Wikimedia\Services\CannotReplaceActiveServiceException
::class );
406 $services->addServiceManipulator( $name, function () {
411 public function testDisableService() {
412 $services = $this->newServiceContainer( [ 'Foo' ] );
414 $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService
::class )
416 $destructible->expects( $this->once() )
417 ->method( 'destroy' );
419 $services->defineService( 'Foo', function () use ( $destructible ) {
420 return $destructible;
422 $services->defineService( 'Bar', function () {
423 return new stdClass();
425 $services->defineService( 'Qux', function () {
426 return new stdClass();
429 // instantiate Foo and Bar services
430 $services->getService( 'Foo' );
431 $services->getService( 'Bar' );
433 // disable service, should call destroy() once.
434 $services->disableService( 'Foo' );
436 // disabled service should still be listed
437 $this->assertContains( 'Foo', $services->getServiceNames() );
439 // getting other services should still work
440 $services->getService( 'Bar' );
442 // disable non-destructible service, and not-yet-instantiated service
443 $services->disableService( 'Bar' );
444 $services->disableService( 'Qux' );
446 $this->assertNull( $services->peekService( 'Bar' ) );
447 $this->assertNull( $services->peekService( 'Qux' ) );
449 // disabled service should still be listed
450 $this->assertContains( 'Bar', $services->getServiceNames() );
451 $this->assertContains( 'Qux', $services->getServiceNames() );
453 $this->setExpectedException( Wikimedia\Services\ServiceDisabledException
::class );
454 $services->getService( 'Qux' );
457 public function testDisableService_fail_undefined() {
458 $services = $this->newServiceContainer();
460 $theService = new stdClass();
461 $name = 'TestService92834576';
463 $this->setExpectedException( Wikimedia\Services\NoSuchServiceException
::class );
465 $services->redefineService( $name, function () use ( $theService ) {
470 public function testDestroy() {
471 $services = $this->newServiceContainer();
473 $destructible = $this->getMockBuilder( Wikimedia\Services\DestructibleService
::class )
475 $destructible->expects( $this->once() )
476 ->method( 'destroy' );
478 $services->defineService( 'Foo', function () use ( $destructible ) {
479 return $destructible;
482 $services->defineService( 'Bar', function () {
483 return new stdClass();
486 // create the service
487 $services->getService( 'Foo' );
489 // destroy the container
490 $services->destroy();
492 $this->setExpectedException( Wikimedia\Services\ContainerDisabledException
::class );
493 $services->getService( 'Bar' );